HBase region split 的一次排查

源于一个已经做了预分区的表,region发生了分裂

  1. 定位发生的时间, 是不是在load hfile的时候 ?

  2. 发生的原因, 是不是单个hfile太大了 ?

  3. 后续该如何避免?

以下分析基于hbase branch-1.2的分支

1. 定位发生的时间

通过对比导入hfiles的时间和发生split的时间,可以很明显的看出,不是在load的时候出的问题, 通过查看regionserver 的日志,可以看到以下几条:

2018-12-04 01:43:50,505 INFO org.apache.hadoop.hbase.regionserver.CompactSplitThread: Completed compaction xxx
2018-12-04 01:43:50,571 INFO org.apache.hadoop.hbase.regionserver.SplitTransaction: Starting split of region xxx
2018-12-04 01:43:51,252 INFO org.apache.hadoop.hbase.regionserver.SplitTransaction: Preparing to split 1 storefiles for region xxx
2018-12-04 01:43:52,669 INFO org.apache.hadoop.hbase.regionserver.SplitRequest: Region split, hbase:meta updated, and report to master. Parent= xxx

可以看出是在做完compact之后,触发了split 的检查,然后检查下来要split才有了上面的日志, 具体源码如下:

收到compact的请求之后,起一个CompactionRunner来跑真正的compact

// org.apache.hadoop.hbase.regionserver.CompactSplitThread
private synchronized CompactionRequest requestCompactionInternal(final Region r, final Store s, final String why, int priority, CompactionRequest request, boolean selectNow, User user) {
    // ....
    ThreadPoolExecutor pool = (selectNow && s.throttleCompaction(compaction.getRequest().getSize()))
      ? longCompactions : shortCompactions;
    pool.execute(new CompactionRunner(s, r, compaction, pool, user));
    // ....
}

doCompaction 里面进行compactsplit检查及split

// CompactionRunner
private void doCompaction(User user) {
    // ....
    long start = EnvironmentEdgeManager.currentTime();
    boolean completed =
        region.compact(compaction, store, compactionThroughputController, user);
    long now = EnvironmentEdgeManager.currentTime();
    LOG.info(((completed) ? "Completed" : "Aborted") + " compaction: " +
          this + "; duration=" + StringUtils.formatTimeDiff(now, start));
    if (completed) {
      // degenerate case: blocked regions require recursive enqueues
      if (store.getCompactPriority() <= 0) {
        requestSystemCompaction(region, store, "Recursive enqueue");
      } else {
        // see if the compaction has caused us to exceed max region size
        requestSplit(region);
      }
    }
    // ....
}

在做compact的线程里面再起一个线程去执行split

public synchronized void requestSplit(final Region r, byte[] midKey, User user) {
    // ...
    this.splits.execute(new SplitRequest(r, midKey, this.server, user));
    // ...
}

2. 定位发生的原因

从上面可以知道,是因为compact 之后触发了split, 现在需要知道为什么split检查通过了 ?

public synchronized boolean requestSplit(final Region r) {
  // don't split regions that are blocking
  if (shouldSplitRegion() && ((HRegion)r).getCompactPriority() >= Store.PRIORITY_USER) {
    byte[] midKey = ((HRegion)r).checkSplit();
    if (midKey != null) {
      requestSplit(r, midKey);
      return true;
    }
  }
  return false;
}

要进入requestSplit需要满足2个条件:

  1. shouldSplitRegion() && ((HRegion)r).getCompactPriority() >= Store.PRIORITY_USER
  2. midKey != null (midKey = ((HRegion)r).checkSplit())

可以肯定的是发生了split的话,上述2个条件一定都满足了

1. 检查shouldSplitRegion

private boolean shouldSplitRegion() {
  if(server.getNumberOfOnlineRegions() > 0.9*regionSplitLimit) {
    LOG.warn("Total number of regions is approaching the upper limit " + regionSplitLimit + ". "
        + "Please consider taking a look at http://hbase.apache.org/book.html#ops.regionmgt");
  }
  return (regionSplitLimit > server.getNumberOfOnlineRegions());
}

以上代码涉及到2个变量:

  1. server.getNumberOfOnlineRegions(): 当前regionserver的在线region的个数
  2. regionSplitLimit: 整个hbase集群中region数的上限, regionSplitLimit = conf.getInt(REGION_SERVER_REGION_SPLIT_LIMIT, DEFAULT_REGION_SERVER_REGION_SPLIT_LIMIT)

因为当前hbase设置了整个参数并且非常大, 所以这个检查是通过的

2. 检查checkSplitmidKey

public byte[] checkSplit() {
  // Can't split META
  if (this.getRegionInfo().isMetaTable() ||
      TableName.NAMESPACE_TABLE_NAME.equals(this.getRegionInfo().getTable())) {
    if (shouldForceSplit()) {
      LOG.warn("Cannot split meta region in HBase 0.20 and above");
    }
    return null;
  }

  // Can't split region which is in recovering state
  if (this.isRecovering()) {
    LOG.info("Cannot split region " + this.getRegionInfo().getEncodedName() + " in recovery.");
    return null;
  }

  if (!splitPolicy.shouldSplit()) {
    return null;
  }

  byte[] ret = splitPolicy.getSplitPoint();

  if (ret != null) {
    try {
      checkRow(ret, "calculated split");
    } catch (IOException e) {
      LOG.error("Ignoring invalid split", e);
      return null;
    }
  }
  return ret;
}

从上面可以看到重点在 splitPolicy.shouldSplit(), 而这个splitPolicy 因为建表的时候,没有指定,所以用的是默认的,即: IncreasingToUpperBoundRegionSplitPolicy

@Override
protected boolean shouldSplit() {
  boolean force = region.shouldForceSplit();
  boolean foundABigStore = false;
  // Get count of regions that have the same common table as this.region
  int tableRegionsCount = getCountOfCommonTableRegions();
  // Get size to check
  long sizeToCheck = getSizeToCheck(tableRegionsCount);

  for (Store store : region.getStores()) {
    // If any of the stores is unable to split (eg they contain reference files)
    // then don't split
    if (!store.canSplit()) {
      return false;
    }

    // Mark if any store is big enough
    long size = store.getSize();
    if (size > sizeToCheck) {
      LOG.debug("ShouldSplit because " + store.getColumnFamilyName() + " size=" + size
                + ", sizeToCheck=" + sizeToCheck + ", regionsWithCommonTable="
                + tableRegionsCount);
      foundABigStore = true;
    }
  }

  return foundABigStore | force;
}

从上面可以看出在对region下的每个store做检查,来判断是不是进行split, 这个判断中用到了3个变量:

  1. tableRegionsCount: 在当前这个regionserver上的,当前表的region个数
  2. sizeToCheck: 获取一个文件大小的阈值, 这个阈值不止和当前集群的最大hfile的大小有关,还和initialSize, tableRegionsCount有关
  3. size: 当前hfile 的大小
protected long getSizeToCheck(final int tableRegionsCount) {
  // safety check for 100 to avoid numerical overflow in extreme cases
  return tableRegionsCount == 0 || tableRegionsCount > 100
             ? getDesiredMaxFileSize()
             : Math.min(getDesiredMaxFileSize(),
                        initialSize * tableRegionsCount * tableRegionsCount * tableRegionsCount);
}

这个方法需要着重注意一下:

  1. getDesiredMaxFileSize(): 获取的是一般是hbase.hregion.max.filesize, 默认10g

  2. initialSize: 一般是2倍的hbase.hregion.memstore.flush.size, 默认128M, 我们这边自己的环境是256M

  3. \text{tableRegionsCount}^3: 这个参数比较坑, 当 tableRegionsCount=1的时候为1, 当tableRegionsCount>1的时候很大

根据这些,我带入了当时集群的参数算了一下:

  1. getDesiredMaxFileSize(): 10g
  2. initialSize: 256M
  3. tableRegionsCount: 2

这样算下来: sizeToCheck=4g, size通过hfds命令看下来也就1.4g左右, 远远达不到split的要求

这里犯了个错误, tableRegionsCount, 看的是split之后的情况, 应该是split之前的情况, 这时候看了下HBase UI, 发现了问题:

hbase_region_split_vs_nonsplit_v2.jpg

可以看出,发生了splitregionserver B,之前应该是只有1个region的, 这样我们重新算一下: sizeToCheck=512M, 这样就会发生分裂了

而当每个regionserver上的region达到2个或以上的时候,一般就不再继续split,因为此时的阈值在4g以上了

3. 后续该怎么避免?

最省心的当然是在建表的时候,设置splitPolicyDisabledRegionSplitPolicy, 搭配预分区效果最好

alter 'table', {METADATA => {'SPLIT_POLICY' => 'org.apache.hadoop.hbase.regionserver.DisabledRegionSplitPolicy'}}

如果数据每天增量很大,就不适合这种方式, 需要参照以上split的分析, 选择region的个数,splitPolicy 来一起处理

ref: https://issues.apache.org/jira/browse/HBASE-16076?attachmentOrder=asc

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容